package lang.statements;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * mock switch-case statement
 * Not threadsafe
 * @param <V>
 */
public class Switcher<V> implements Runnable {
    private final static int STATEMENT_INITIAL = -1;
    private final static int STATEMENT_WHEN = 0;
    private final static int STATEMENT_THEN = 1;
    private final static int STATEMENT_BREAK = 2;
    private final static int STATEMENT_DEFAULT = 3;
    private boolean defaultOccur = false;
    private int lastStatement = STATEMENT_INITIAL;
    private V value;
    private int index = 0;
    private int casesIndex = 0;
    private List<Integer> caseIndexes = new ArrayList<>();
    private List<Runnable> runnableList = new ArrayList<>();

    private boolean matched;

    private final static Runnable BREAK = () -> {
        //Do nothing to terminate
    };

    private void runNext() {
        if (index <= runnableList.size() - 1) {
            runnableList.get(index++).run();
        }
    }

    private Switcher(V v) {
        this.value = v;
    }

    public static <V> Switcher<V> of(V v) {
        return new Switcher<>(v);
    }

    public Switcher<V> when(V v, Runnable then) {
        addWhen(v);
        caseIndexes.add(runnableList.size() - 1);
        runnableList.add(() -> {
            then.run();
            Switcher.this.runNext();
        });
        lastStatement = STATEMENT_WHEN;
        return this;
    }

    @SafeVarargs
    public final Switcher<V> when(V v, V... vs) {
        addWhen(v);
        for (V val : vs) {
            addWhen(val);
        }
        lastStatement = STATEMENT_WHEN;
        return this;
    }

    private void addWhen(V v) {
        runnableList.add(() -> {
            casesIndex++;
            if (matched || (matched = Objects.equals(v, value))) {
                this.runNext();
            } else {
                this.runNextCase();
            }
        });
        caseIndexes.add(runnableList.size() - 1);
    }

    public Switcher<V> then(Runnable then) {
        runnableList.add(() -> {
            then.run();
            Switcher.this.runNext();
        });
        lastStatement = STATEMENT_THEN;
        return this;
    }

    public Switcher<V> doBreak() {
        if (lastStatement == STATEMENT_BREAK) {
            throw new IllegalArgumentException("break unreachable!Because last statement is break");
        }
        runnableList.add(BREAK);
        lastStatement = STATEMENT_BREAK;
        return this;
    }

    private void runNextCase() {
        if (casesIndex <= caseIndexes.size() - 1) {
            index = caseIndexes.get(casesIndex);
            runNext();
        }
    }

    private void checkDefault() {
        if (defaultOccur) {
            throw new IllegalStateException("switch已经出现default");
        }
    }

    public Switcher<V> onDefault(Runnable runnable) {
        checkDefault();
        runnableList.add(() -> {
            runnable.run();
            this.runNext();
        });
        caseIndexes.add(runnableList.size() - 1);
        defaultOccur = true;
        lastStatement = STATEMENT_DEFAULT;
        return this;
    }

    public void run() {
        runNext();
    }
}
